using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace S
{
    public class FlowLayoutP : System.Windows.Forms.FlowLayoutPanel
    {
        private class ControlInfo
        {
            public ControlInfo()
            {
                this.mIndexInParent = 0;
                this.mSnapRectangle = new Rectangle();
            }

            public int IndexInParent
            {
                get
                {
                    return this.mIndexInParent;
                }

                set
                {
                    this.mIndexInParent = value;
                }
            }

            public Rectangle SnapRectangle
            {
                get
                {
                    return this.mSnapRectangle;
                }
                set
                {
                    this.mSnapRectangle = value;
                }
            }

            private Rectangle mSnapRectangle;
            private int mIndexInParent;
        }

        /*public string DebugText
        {
            get
            {
                return this.mDebugText;
            }
        }*/

        public FlowLayoutP()
        {
            this.FlowDirection = FlowDirection.TopDown;
            this.WrapContents = false;
            //this.AutoSize = true;
            this.AutoScroll = true;
            this.Padding = new Padding(12, 12, 0, 0);
            this.DoubleBuffered = true;

            this.mCarot = new Bitmap("../../arrow.png");
            //this.mCarot = new Bitmap(11, 9);
            this.mSnapControlInfo = new ControlInfo();

            this.mDebugLabel = new Label();
            this.mDebugLabel.Name = "Label Debug";
            this.mDebugLabel.BackColor = Color.LemonChiffon;
            this.mDebugLabel.Size = new Size(240, 24);
            this.Controls.Add(this.mDebugLabel);

            this.ControlAdded += new ControlEventHandler(FlowLayoutP_ControlAdded);
            this.MouseMove += new MouseEventHandler(FlowLayoutP_MouseMove);
        }

        void FlowLayoutP_MouseMove(object sender, MouseEventArgs e)
        {
            this.SetDebugText(e.Location.ToString());
            //this.SetDebugText("I HAVE CONTROL");
        }

        void FlowLayoutP_ControlAdded(object sender, ControlEventArgs e)
        {
            Type t = e.Control.GetType();

            e.Control.MouseDown += Control_MouseDown;
            e.Control.MouseUp += Control_MouseUp;
            e.Control.MouseMove += Control_MouseMove;

            foreach (Control control in e.Control.Controls)
            {
                control.MouseDown += Control_MouseDown;
                control.MouseUp += Control_MouseUp;
                control.MouseMove += Control_MouseMove;
            }

            e.Control.MouseEnter += Control_MouseEnter;
            e.Control.MouseClick += new MouseEventHandler(Control_MouseClick);
        }

        void Control_MouseClick(object sender, MouseEventArgs e)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        void Control_MouseEnter(object sender, EventArgs e)
        {
            Control c = (Control)sender;
            this.SetDebugText(c.Name + ": " + c.Bounds.ToString() );
        }

        void Control_MouseUp(object sender, MouseEventArgs e)
        {
            Control c = (Control)sender;
            this.SetDebugText("Mouse Up: " + c.Name);

            // only apply the reordering if were still within the snap rectangle
            if (!this.mSnapControlInfo.SnapRectangle.IsEmpty)
            {
                // reorder the object to reflect the move
                this.Controls.SetChildIndex(this.mLiveControl, this.mSnapControlInfo.IndexInParent);
                
                // refresh ourselves to hide the carot
                Invalidate();

                // turn our snapping rectangle off so the mouse move calculations will work properly
                this.mSnapControlInfo.SnapRectangle = Rectangle.Empty;
            }

            this.mLiveControl = null;
            
        }

        void Control_MouseDown(object sender, MouseEventArgs e)
        {
            Control control = (Control)sender;
            
            while (control.Parent.GetType() != typeof(S.FlowLayoutP))
            {
                control = control.Parent;
            }

            this.SetDebugText("Mouse Down: " + control.Name);
            this.mLiveControl = control;
        }

        void Control_MouseMove(object sender, MouseEventArgs e)
        {

            if (mLiveControl != null)
            {
                this.SetDebugText("Dragging: " + ((Control)sender).Name);

                Control sentControl = (Control)sender;
                Point location = e.Location;

                do
                {
                    location.X += sentControl.Location.X;
                    location.Y += sentControl.Location.Y;
                    sentControl = sentControl.Parent;
                }
                while (sentControl.GetType() != typeof(S.FlowLayoutP));

                

                // check if there is a snapping rectangle that live
                if (!this.mSnapControlInfo.SnapRectangle.IsEmpty)
                {
                    // there is a snap rectangle lets check if the current point is still in it
                    if (!this.mSnapControlInfo.SnapRectangle.Contains(location))
                    {
                        // it is not in here anymore so reset the snap rectangle
                        this.mSnapControlInfo.SnapRectangle = Rectangle.Empty;
                        
                        // also refresh to hide the carot weve been drawing
                        this.Invalidate();
                    }
                }

                // check if mouse overed anything else
                for (int i = 0; i < this.Controls.Count; i++)
                {
                    Control control = this.Controls[i];

                    if (control == this.mLiveControl)
                    {
                        continue;
                    }

                    Rectangle snapRect;
                    Point carotPosition;

                    if (control.Bounds.Contains(location))
                    {
                        // check if moving up
                        if (e.Location.Y < 0)
                        {
                            snapRect = new Rectangle(control.Location, new Size(control.Width, control.Height / 2));
                            carotPosition = new Point(control.Location.X - this.mCarot.Width, control.Location.Y - 8);
                        }

                        // or moving down
                        else
                        {
                            snapRect = new Rectangle(new Point(control.Location.X, control.Location.Y + control.Height / 2), new Size(control.Width, control.Height / 2));
                            carotPosition = new Point(control.Location.X - this.mCarot.Width, control.Location.Y + control.Height - 1);
                        }

                        if (snapRect.Contains(location))
                        {
                            // draw the carot
                            this.CreateGraphics().DrawImage(this.mCarot, carotPosition);

                            this.SetDebugText("Over:" + control.Name);

                            // save the snap rectangle and index
                            this.mSnapControlInfo.SnapRectangle = snapRect;
                            this.mSnapControlInfo.IndexInParent = i;

                            // we are already snapped on something and we cant snap on two different things
                            // at the same time so just exit the loop
                            break;
                        }
                    }
                }
            }
        }

        private void SetDebugText(string pDebugText)
        {
            this.mDebugLabel.Text = pDebugText;
        }

        private Label mDebugLabel;
        private Control mLiveControl;

        private ControlInfo mSnapControlInfo;
        private Bitmap mCarot;
    }
}
